package Porsche911GT2RS;

public class Porsche911GT2RS
{
    // Gewicht in KG
    private final double mMass = 1445.0;
    // Max. Leistung in KW
    private final double mPowerPropMax = 456000.0;
    // Max. Speed in m/s
    private final double mSpeedMax = 91.6;
    // Min. Speed in m/s
    private final double mSpeedMin = 0.0;
    // Erdbeschleunigung in m/(s*s)
    private final double mAccEarth = 9.81;
    // max. Kraft
    private final double mForcePropMax = mMass * mAccEarth;
    // max. DragConst
    private final double mDragConst = Math.abs(mPowerPropMax / Math.pow(mSpeedMax,3));

    // Range für mLevel
    private final double mMinLevel = -1.0;
    private final double mMaxLevel = 1.0;

    // Umrechnug von m/s nach km/h
    private final double meterPerSecToKilometersPerHoursFactor = 3.6;

    // Antriebshebel
    private double mLevel;
    // Zeit in S
    private double mTime;
    // Position
    private double mPos;
    // Geschwindigtkeit
    private double mSpeed;


    // Der Konstruktor
    public Porsche911GT2RS() throws RangeException
    {
        // Variablen auf Default-Werte setzen
        reset();
    }

    // Range prüfen
    // Error => RangeException
    private boolean checkLevelRange(double aLevel) throws RangeException
    {
        // Rückgabe
        boolean bReturn = true;

        // Eingabe im gültigen Bereich?
        if( !((aLevel <= mMaxLevel) && (aLevel >= mMinLevel )) )
        {
            // Nicht aktzeptiert
            bReturn = false;
            throw new RangeException();
        }

        // und ab damit
        return bReturn;
    }

    public static void blubb() {}
    // Setzt alle wichtigen Variablen
    public void set(double aTime, double aPos, double aSpeed, double aLevel) throws RangeException
    {
        if( checkLevelRange(aLevel) )
        {
            mTime  = aTime;
            mPos   = aPos;
            mSpeed = aSpeed;
            mLevel = aLevel;
        }
    }

    // Setzt alle wichtigen Variablen auf Default-Werte zurück
    public void reset() throws RangeException
    {
        set(0.0,0.0,0.0,0.0);
    }

    // Einen Simulations-Schritt ausführen
    public void step(double aDeltaTime, double aLevel) throws RangeException
    {
        // Manipulation nur durchführen, wenn Level gültig ist
        if( checkLevelRange(aLevel) )
        {         
            // Wie viel % der Leistung
            double powerProp = mPowerPropMax * Math.abs(mLevel);

             // Formeln anwenden
            double forcePropAbs = Math.min(mForcePropMax, powerProp/(Math.max(0.0001, Math.abs(mSpeed))));
            double forceProp = forcePropAbs * Math.signum(mLevel);
            double forceDrag = mDragConst * (mSpeed*mSpeed) * Math.signum(-mSpeed);
            double force = forceProp + forceDrag;
            double acc = force / mMass;

            // Interne Werte setzen
            mTime += aDeltaTime;
            mPos = mPos + (mSpeed * aDeltaTime);
            mLevel = aLevel;
            mSpeed = mSpeed + (acc * aDeltaTime);
        }
    }

    // Aktuelle Geschwindigkeit in km/h
    public double getSpeed()
    {
        return mSpeed * meterPerSecToKilometersPerHoursFactor;
    }

    // Aktuelle Position in Metern
    public double getPos()
    {
        return mPos;
    }

    // Aktuelle Simulationszeit in Sekunden
    public double getTime()
    {
        return mTime;
    }
}

// Unsere Fehlermeldung, wenn die Bereichsgrenzen überschritten werden
class RangeException extends Exception
{
    RangeException()
    {
        super("This value is not allowed.");
    }
}